#include <tvm/tvm_preprocessor.h>
#include <tvm/tvm_file.h>
#include <tvm/tvm_tokens.h>
#include <unistd.h>
#include <string.h>

// include 预处理器
static int process_includes(char **src, int *src_len, struct tvm_htab_ctx *defines)
{
    // 在当前一行查找“%include”第一次出现的位置
    char *pp_directive_delimiter = strstr(*src, TOK_INCLUDE);

    // 没找到则返回
    if (!pp_directive_delimiter)
        return 0;

    // 获取“%include”开始位置，同时获取这一行的结束位置
    char *begin = pp_directive_delimiter,
         *end = strchr(begin, '\n');

    if (!end || !begin)
        return 0;

    // 根据开始和结束位置，获得长度
    int linelen = end - begin;
    // 根据长度申请空间
    char *temp_str = calloc(linelen + 1, sizeof(char));

    // 将字符串拷贝到这个空间上
    memcpy(temp_str, begin, linelen);

    // 获取%include [temp_str]这个temp_str文件名
    char *filename = (strchr(temp_str, ' ') + 1);
    // 以只读方式打开这个[temp_str].vm的文件
    FILE *filp = tvm_fopen(filename, ".vm", "r");

    if (!filp)
    {
        printf("Unable to open file \"%s\"\n", filename);
        free(temp_str);
        return -1;
    }

    // 释放这个存储文件名的临时空间
    free(temp_str);

    // 获取文件长度
    size_t addition_len = tvm_flength(filp);
    // 根据文件文本长度申请地址空间
    char *addition_str = calloc(addition_len, sizeof(char));
    // 将文件内容拷贝到这个地址空间上
    tvm_fcopy(addition_str, addition_len, filp);
    // 关闭文件
    fclose(filp);

    // 下面3行的作用是找到%include的开始位置
    // 找到%include这一行的结束位置
    // 将开始位置+这个文件大小+结束位置
    // 相当于在原来的文件里面插入include的这个文件的内容
    size_t first_block_len = (begin - *src);
    size_t second_block_len = ((*src + *src_len) - end);
    size_t new_src_len = (first_block_len + addition_len + second_block_len);

    // 得到当前文件长度，根据这个长度申请空间
    char *new_src = (char *)calloc(sizeof(char), new_src_len);
    // 将开始处内容拷贝过去
    strncpy(new_src, (*src), first_block_len);
    // 将新增的内容拷贝过去
    strcat(new_src, addition_str);
    // 将末尾的内容拷贝过去
    strcat(new_src, end + 1);
    // 释放原来的地址空间
    free(*src);
    // 将新申请的地址空间返回
    *src = new_src;
    // 返回当前新的文件长度
    *src_len = strlen(*src);
    return 1;
}

// define 预处理器
static int process_defines(char **src, int *src_len, struct tvm_htab_ctx *defines)
{
    char *pp_directive_delimiter = strstr(*src, TOK_DEFINE);

    if (!pp_directive_delimiter)
        return 0;

    char *begin = pp_directive_delimiter;
    char *end = strchr(begin, '\n');

    if (!end)
        return 0;

    // 获取dedine指令长度
    int offset = strlen(TOK_DEFINE) + 1;
    // %define \n
    // 0 + 8 8 这种情况说明define后面没有值
    // %deing aa 1 \n 这种情况才是正常的
    if (begin + offset >= end)
    {
        printf("Define missing arguments.\n");
        return -1;
    }

    // %dfine [aa 1]\n这一部分就是length的值
    int length = (end - (begin + offset));
    // 根据这部分长度申请地址空间，+1是'\0'
    char tempstr[length + 1];
    // 空间重置
    memset(tempstr, 0, length + 1);
    // 空间内容复制
    memcpy(tempstr, begin + offset, length);

    char *keystr = tempstr;
    // 根据空格分割为key和value
    char *valstr = strchr(tempstr, ' ');

    /* If there is a value, seperate the key and value
	 * with a null character.
	 */
    if (valstr)
    {
        *valstr = 0; // ‘ ’空格置0
        valstr += 1; // 地址+1，移到下一个value第一个字符的位置上
    }

    if (!keystr || !valstr)
    {
        printf("Define missing arguments.\n");
        return -1;
    }

    // 根据key判断是否存在这个key
    if (tvm_htab_find(defines, keystr) < 0)
        // 不存在则新增
        tvm_htab_add_ref(defines, keystr, valstr, strlen(valstr) + 1);
    else
    {
        printf("Multiple definitions for %s.\n", keystr);
        return -1;
    }

    /* Remove the define line so it is not processed again. */
    size_t new_src_len = *src_len - (end - begin);
    size_t first_block_len = begin - *src;
    size_t second_block_len = (*src + *src_len) - end;
    // 定义define的这一行已经处理过了，所以应该删掉这一行，防止被重复处理
    memmove(&(*src)[first_block_len], end, second_block_len);
    // 删除掉这一行后，重新调整空间大小
    *src = realloc(*src, sizeof(char) * new_src_len);
    // 返回调整后空间长度
    *src_len = new_src_len;

    return 1;
}

static int preprocess_pass(char **src, int *src_len, struct tvm_htab_ctx *defines)
{
    int ret = 0;

    // include预处理
    ret += process_includes(src, src_len, defines);
    // define预处理
    ret += process_defines(src, src_len, defines);
    return ret;
}

// **src此时是字符串值，而不是指针
int tvm_preprocess(char **src, int *src_len, struct tvm_htab_ctx *defines)
{
    int ret = 1;

    while (ret > 0)
        ret = preprocess_pass(src, src_len, defines);

    return ret;
}
